//===- PreferenceOptions.cpp ----------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/PreferenceOptions.h>
#include <mcld/LinkerConfig.h>
#include <mcld/Support/CommandLine.h>
#include <mcld/Support/raw_ostream.h>
#include <llvm/Support/Process.h>

#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif

#if defined(_MSC_VER) || defined(__MINGW32__)
#include <io.h>
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
#define STDERR_FILENO 2
#endif
#endif

namespace {

llvm::cl::opt<bool> ArgTrace(
    "t",
    llvm::cl::desc("Print the names of the input files as ld processes them."));

llvm::cl::alias ArgTraceAlias("trace",
                              llvm::cl::desc("alias for -t"),
                              llvm::cl::aliasopt(ArgTrace));

llvm::cl::opt<int> ArgVerbose(
    "verbose",
    llvm::cl::init(-1),
    llvm::cl::desc(
        "Display the version number for ld and list the\n"
        "linker emulations supported."));

llvm::cl::opt<bool> ArgVersion(
    "Version",
    llvm::cl::init(false),
    llvm::cl::desc("Display the version number for MCLinker."));

llvm::cl::alias ArgVersionAlias("v",
                                llvm::cl::desc("alias for -Version"),
                                llvm::cl::aliasopt(ArgVersion));

llvm::cl::opt<int> ArgMaxErrorNum(
    "error-limit",
    llvm::cl::init(-1),
    llvm::cl::desc("limits the maximum number of erros."));

llvm::cl::opt<int> ArgMaxWarnNum(
    "warning-limit",
    llvm::cl::init(-1),
    llvm::cl::desc("limits the maximum number of warnings."));

llvm::cl::opt<mcld::PreferenceOptions::Color> ArgColor(
    "color",
    llvm::cl::value_desc("When"),
    llvm::cl::desc("Surround the result strings with the marker"),
    llvm::cl::init(mcld::PreferenceOptions::COLOR_Auto),
    llvm::cl::values(
        clEnumValN(mcld::PreferenceOptions::COLOR_Never,
                   "never",
                   "do not surround result"),
        clEnumValN(mcld::PreferenceOptions::COLOR_Always,
                   "always",
                   "always surround results, even the output is a plain file"),
        clEnumValN(mcld::PreferenceOptions::COLOR_Auto,
                   "auto",
                   "surround result strings only if the output is a tty"),
        clEnumValEnd));

llvm::cl::opt<bool> ArgPrintMap(
    "M",
    llvm::cl::desc("Print a link map to the standard output."),
    llvm::cl::init(false));

llvm::cl::alias ArgPrintMapAlias("print-map",
                                 llvm::cl::desc("alias for -M"),
                                 llvm::cl::aliasopt(ArgPrintMap));

bool ArgFatalWarnings;

llvm::cl::opt<bool, true, llvm::cl::FalseParser> ArgNoFatalWarnings(
    "no-fatal-warnings",
    llvm::cl::location(ArgFatalWarnings),
    llvm::cl::desc("do not turn warnings into errors"),
    llvm::cl::init(false),
    llvm::cl::ValueDisallowed);

llvm::cl::opt<bool, true> ArgFatalWarningsFlag(
    "fatal-warnings",
    llvm::cl::ZeroOrMore,
    llvm::cl::location(ArgFatalWarnings),
    llvm::cl::desc("turn all warnings into errors"),
    llvm::cl::init(false),
    llvm::cl::ValueDisallowed);

llvm::cl::opt<std::string> ArgUseLD(
    "fuse-ld",
    llvm::cl::desc("Ignored for GCC/collect2 linker compatibility."),
    llvm::cl::init("mcld"));

llvm::cl::opt<bool> ArgUseMCLD(
    "use-mcld",
    llvm::cl::desc("Ignored for GCC/collect2 linker compatibility."),
    llvm::cl::init(false));

//===----------------------------------------------------------------------===//
// Non-member functions
//===----------------------------------------------------------------------===//
inline bool ShouldColorize() {
  const char* term = getenv("TERM");
  return term && (0 != strcmp(term, "dumb"));
}

}  // anonymous namespace

using namespace mcld;

//===----------------------------------------------------------------------===//
// PreferenceOptions
//===----------------------------------------------------------------------===//
PreferenceOptions::PreferenceOptions()
    : m_Trace(ArgTrace),
      m_Verbose(ArgVerbose),
      m_Version(ArgVersion),
      m_MaxErrorNum(ArgMaxErrorNum),
      m_MaxWarnNum(ArgMaxWarnNum),
      m_Color(ArgColor),
      m_PrintMap(ArgPrintMap),
      m_FatalWarnings(ArgFatalWarnings) {
}

bool PreferenceOptions::parse(LinkerConfig& pConfig) {
  // set -t
  pConfig.options().setTrace(m_Trace);

  // set --verbose
  pConfig.options().setVerbose(m_Verbose);

  // set --error-limit [number]
  pConfig.options().setMaxErrorNum(m_MaxErrorNum);

  // set --warning-limit [number]
  pConfig.options().setMaxWarnNum(m_MaxWarnNum);

  // set --color [mode]
  switch (m_Color) {
    case COLOR_Never:
      pConfig.options().setColor(false);
      break;
    case COLOR_Always:
      pConfig.options().setColor(true);
      break;
    case COLOR_Auto:
      bool color_option =
          ShouldColorize() &&
          llvm::sys::Process::FileDescriptorIsDisplayed(STDOUT_FILENO);
      pConfig.options().setColor(color_option);
      break;
  }

  mcld::outs().setColor(pConfig.options().color());
  mcld::errs().setColor(pConfig.options().color());

  if (m_Version)
    mcld::outs() << pConfig.options().getVersionString() << "\n";

  return true;
}
